﻿using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Emit;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using Volo.Abp;
using Volo.Abp.Application.Services;
using Volo.Abp.DependencyInjection;
using MD5 = System.Security.Cryptography.MD5;

namespace YaAppoint.RoslynCompiler
{
    /// <summary>
    /// 动态编译服务
    /// </summary>
    [RemoteService(isEnabled:false)]
    public class DynamicCompilerAppService : ApplicationService, IDynamicCompilerAppService, ITransientDependency
    {
        private readonly IServiceProvider _serviceProvider;
        private readonly List<MetadataReference> _references = new List<MetadataReference>();

        public DynamicCompilerAppService(IServiceProvider serviceProvider)
        {
            _serviceProvider = serviceProvider;
            AddLoadedAssemblies();
        }

        // 加载当前域中所有已被加载的程序集
        public void AddLoadedAssemblies(Func<Assembly, bool>? filter = null)
        {
            var assembiles = AppDomain.CurrentDomain.GetAssemblies()
                .Where(a => !a.IsDynamic && !string.IsNullOrEmpty(a.Location));

            if (filter != null)
            {
                assembiles = assembiles.Where(filter);
            }

            foreach (var assembly in assembiles)
            {
                AddReferences(assembly);
            }
        }

        /// <summary>
        /// 添加程序集引用
        /// </summary>
        /// <param name="assembly">程序集对象</param>
        public void AddReferences(Assembly assembly)
        {
            _references.Add(MetadataReference.CreateFromFile(assembly.Location));
        }

        /// <summary>
        /// 编译并执行
        /// </summary>
        /// <param name="code">C#脚本字符串</param>
        /// <param name="typeName">要编译的类型名称</param>
        /// <returns></returns>
        public Type CompileAndGetType(string code, string typeName, out DynamicAssemblyLoadContext assemblyLoadContext)
        {
            // 语法树对象
            SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(code);

            // 编译选项对象
            CSharpCompilationOptions options = new CSharpCompilationOptions(
                OutputKind.DynamicallyLinkedLibrary,
                optimizationLevel: OptimizationLevel.Release, // 动态编译，直接就是发布模式下的优化，因为动态编译不涉及断点调试
                allowUnsafe: true
            );

            // 编译对象
            CSharpCompilation compilation = CSharpCompilation.Create(
                  $"DynamicCompilation_{Guid.NewGuid()}",
                  new[] { syntaxTree },
                  _references,
                  options
            );

            // 在内存中编译   
            using (MemoryStream ms = new MemoryStream())
            {
                EmitResult result = compilation.Emit(ms);  // 触发编译

                if (!result.Success) // 编译失败    diagnostic:诊断      severity:严重程度，严重性     
                {
                    IEnumerable<Diagnostic> failures = result.Diagnostics.Where(diagnostic => diagnostic.IsWarningAsError || diagnostic.Severity == DiagnosticSeverity.Error);

                    string errMessage = string.Join(Environment.NewLine, failures.Select(f => f.ToString()));
                    throw new Exception($"动态编译错误:{errMessage}");
                }

                ms.Seek(0, SeekOrigin.Begin);

                // 加载编译后的程序集
                assemblyLoadContext = new DynamicAssemblyLoadContext();
                Assembly assembly = assemblyLoadContext.LoadFromStream(ms);
                Type? entryType = assembly.GetType(typeName);
                if (entryType == null)
                {
                    throw new BusinessException(CustomErrorCodes.DynamicCompileEntryTypeNotFound)
                    {
                        Data = {
                             { "typeName",typeName}
                        }
                    };
                }
                return entryType;
            }
        }

        /// <summary>
        /// 创建实例，并从IOC中解析依赖注入
        /// </summary>
        /// <param name="code">代码内容</param>
        /// <param name="typeNmae">类型名</param>
        /// <returns></returns>
        public object CreateInstanceWithDI(string code, string typeName,out DynamicAssemblyLoadContext assemblyLoadContext )
        {
            Type type = CompileAndGetType(code, typeName,out assemblyLoadContext);

            // 获取构造函数(参数数量最多的)
            ConstructorInfo? constructor = type.GetConstructors().OrderByDescending(c => c.GetParameters().Length)
                                              .FirstOrDefault();

            if (constructor == null)
            {
                throw new BusinessException(CustomErrorCodes.NoSuitableConstructFound)
                {
                    Data = {
                        { "typeName",typeName}
                    }
                };
            }

            // 解析构造函数参数
            ParameterInfo[] parameters = constructor.GetParameters();
            object?[] parameterValues = new object?[parameters.Length];

            for (int i = 0; i < parameters.Length; i++)
            {
                parameterValues[i] = _serviceProvider.GetService(parameters[i].ParameterType);
            }

            // 调用依赖注入构造函数，创建带有依赖注入的实例对象
            return constructor.Invoke(parameterValues);
        }

        /// <summary>
        /// 计算线上代码MD5
        /// </summary>
        public string GetCodeMD5(string code)
        {
            if (string.IsNullOrEmpty(code))
            {
                throw new BusinessException(CustomErrorCodes.RuleCodeIsNullOrEmpty);
            }
            using var md5 = MD5.Create();
            byte[] inputBytes = Encoding.UTF8.GetBytes(code);
            //计算哈希值
            byte[] hashBytes = md5.ComputeHash(inputBytes);
            // 将字节数组转化为十六进制字符串
            StringBuilder sb = new StringBuilder();
            foreach (Byte b in hashBytes)
            {
                // 格式化为两位十六进制数，不足两位前面补0
                sb.Append(b.ToString("x2"));
            }

            return sb.ToString();
        }
    }
}
